﻿using System;
using System.Collections.Generic;
using System.IO;
using Lucene.Net.Documents;
using Microsoft.TeamFoundation.VersionControl.Client;
using NLog;

namespace TfsGiggle.Core
{
    /// <summary>
    /// This is what is indexed
    /// </summary>
    public class SourceControlDocument
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Fieldnames for lucene documents
        /// </summary>
        public class FieldNames
        {
            /// <summary>
            /// Modified date
            /// </summary>
            public const string Modified = "modified";

            /// <summary>
            /// Tokens for content
            /// </summary>
            public const string Contents = "contents";

            /// <summary>
            /// Changeset id
            /// </summary>
            public const string ChangesetId = "changeset_id";

            /// <summary>
            /// Comment
            /// </summary>
            public const string ChangesetComment = "changeset_comment";

            /// <summary>
            /// Who made the change
            /// </summary>
            public const string ChangesetCommitter = "changeset_committer";

            /// <summary>
            /// Workitem id
            /// </summary>
            public const string WorkitemId = "workitem_id";

            /// <summary>
            /// Title
            /// </summary>
            public const string WorkitemTitle = "workitem_title";

            /// <summary>
            /// A combined key of teamProjectCollectionId:path
            /// </summary>
            public const string DocumentKey = "documentkey";
        }

        private SourceControlDocument() { }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="item"></param>
        /// <param name="teamProjectCollectionId"></param>
        /// <param name="sourceControlPath"></param>
        /// <returns></returns>
        public static Document Document(Item item, string teamProjectCollectionId, string sourceControlPath)
        {
            if (item == null) throw new ArgumentNullException("item");
            if (string.IsNullOrWhiteSpace(sourceControlPath)) throw new ArgumentNullException("sourceControlPath");
            _logger.Trace("item:{0} teamProjectCollectionId:{1} sourceControlPath':{2}", item.ChangesetId, teamProjectCollectionId, sourceControlPath);

            var vcs = TfsSourceControl.Instance.GetVersionControlServer(teamProjectCollectionId);
            if (vcs == null)
            {
                _logger.Trace("Tfs services are currently unavailable");
                return null;
            }

            if (!vcs.ServerItemExists(sourceControlPath, ItemType.File))
            {
                _logger.Trace("sourceControlPath:{0} does not exist", sourceControlPath);
                return null;
            }

            // make a new, empty document
            Document doc = new Document();

            // Add the documentkey as a combination og teamprojectcollectionid and sourceControlPath
            // Use a field that is indexed (i.e. searchable), but don't tokenize the field into words.
            doc.Add(new Field(SourceControlDocument.FieldNames.DocumentKey, IndexModel.GetDocumentKey(teamProjectCollectionId, sourceControlPath), Field.Store.YES, Field.Index.NOT_ANALYZED));

            // Add the last modified date of the file a field named "modified".  Use 
            // a field that is indexed (i.e. searchable), but don't tokenize the field
            // into words.
            doc.Add(new Field(SourceControlDocument.FieldNames.Modified, item.CheckinDate.ToLongDateString(), Field.Store.YES, Field.Index.NOT_ANALYZED));

            // Specify a Reader,so that the text of the file is tokenized and indexed, but not stored.
            // Note that FileReader expects the file to be in the system's default encoding.
            // If that's not the case searching for special characters will fail.
            doc.Add(new Field(SourceControlDocument.FieldNames.Contents, new StreamReader(item.DownloadFile())));

            var workItems = new Dictionary<int, string>();
            int numberOfChangesets = 0;
            foreach (Changeset changeset in item.VersionControlServer.QueryHistory(sourceControlPath, VersionSpec.Latest, 0, RecursionType.None, "", null, VersionSpec.Latest, Int32.MaxValue, true, false))
            {
                doc.Add(new Field(SourceControlDocument.FieldNames.ChangesetId, changeset.ChangesetId.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
                doc.Add(new Field(SourceControlDocument.FieldNames.ChangesetComment, changeset.Comment, Field.Store.YES, Field.Index.ANALYZED));
                doc.Add(new Field(SourceControlDocument.FieldNames.ChangesetCommitter, changeset.Committer, Field.Store.YES, Field.Index.NOT_ANALYZED));
                TfsWorkItem.AddWorkItems(changeset, workItems);
                numberOfChangesets++;
                _logger.Trace("changeset({0}) added", changeset.ChangesetId);
            }
            _logger.Trace("{0} changesets found for {1}", numberOfChangesets, sourceControlPath);

            foreach (var workItem in workItems)
            {
                doc.Add(new Field(SourceControlDocument.FieldNames.WorkitemId, workItem.Key.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
                doc.Add(new Field(SourceControlDocument.FieldNames.WorkitemTitle, workItem.Value, Field.Store.YES, Field.Index.ANALYZED));
                _logger.Trace("workitem({0}) added", workItem.Key);
            }
            _logger.Trace("{0} distinct workitems found for {1}", workItems.Count, sourceControlPath);
            return doc;
        }
    }
}